package cz.apnetis.fitweb.server;

import java.io.IOException;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.context.ServletContextAware;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.server.rpc.RPC;
import com.google.gwt.user.server.rpc.RPCRequest;
import com.google.gwt.user.server.rpc.RPCServletUtils;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;

public class GWTController extends RemoteServiceServlet implements Controller, ServletContextAware {

  private final static Logger LOG = LoggerFactory.getLogger(GWTController.class); 
  
  // Instance fields
  private RemoteService remoteService;
  private Class<RemoteService> remoteServiceClass;
  private ServletContext servletContext;

  @Override
  @RequestMapping(method = { RequestMethod.GET, RequestMethod.POST })
  public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
    LOG.info("Handle request");

    if (remoteService == null) {
      throw new NullPointerException("Remote service is null");
    }

    doPost(request, response);
    return null; // response handled by GWT RPC over XmlHttpRequest
  }

  @Override
  protected String readContent(HttpServletRequest request)
    throws ServletException, IOException {
    LOG.info("Read content");
    return RPCServletUtils.readContentAsUtf8(request, false);
  }
  
  /**
   * Process the RPC request encoded into the payload string and return a string
   * that encodes either the method return or an exception thrown by it.
   */
  @Override
  public String processCall(String payload) throws SerializationException {
    try {
      RPCRequest rpcRequest = RPC.decodeRequest(payload, this.remoteServiceClass);

      // delegate work to the spring injected service
      return RPC.invokeAndEncodeResponse(this.remoteService, rpcRequest.getMethod(),
                                         rpcRequest.getParameters() );
    } catch (IncompatibleRemoteServiceException e) {
      return RPC.encodeResponseForFailure(null, e);
    }
  }

  /**
   * Setter for Spring injection of the GWT RemoteService object.
   * @param RemoteService the GWT RemoteService implementation
   * that will be delegated to by
   * the {@code GWTController}.
   */
  @SuppressWarnings("unchecked")
  public void setRemoteService( RemoteService remoteService ) {
    this.remoteService = remoteService;
    this.remoteServiceClass = (Class<RemoteService>) this.remoteService.getClass();
  }

  @Override
  public void setServletContext(ServletContext servletContext) {
    this.servletContext = servletContext;
  }
  
  @Override
  public ServletContext getServletContext() {
    return this.servletContext;
  }
}